package de.sendorian.app.forumArchive;


import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.hamcrest.Matcher;

import de.sendorian.app.forumArchive.domain.Forum;
import de.sendorian.app.forumArchive.domain.Topic;

public class Compressor {

	private static Logger logger = Logger.getLogger(Compressor.class.getName());

	private static final String PNGOUT = new File("bin/pngout").getAbsolutePath();


	/**
	 * Compresses all images in the topic folders that matches the filter. This is a workaround
	 * because it is not possible to get informations for the images which were contained in an
	 * archive. So simply all images in that folder will be compressed.
	 *
	 */
	public void compressImages(List<Forum> forums, Matcher<Topic> topicMatcher) {
		Set<String> allImages = new Collector().collectImagesInForumDirs(forums, topicMatcher);
		logger.info("All images collected. Compressing " + allImages.size() + " images");
		compressImages(allImages);
	}


	public void compressImagesInFolders(Set<String> dirs) {
		Set<String> allFiles = new Collector().collectImagesInFolders(dirs);
		logger.info("All images collected. Compressing " + allFiles.size() + " images");
		compressImages(allFiles);
	}


	/**
	 * Compresses all the images in the Set. (The Images have to have a fully qualified path!)
	 *
	 */
	public void compressImages(Set<String> images) {
		int processed = 0;
		for (Iterator<String> it = images.iterator(); it.hasNext();) {
			String image1 = it.next();
			processed++;
			String image2 = null;
			if (it.hasNext()) {
				image2 = it.next();
				processed++;
			}
			compressImage(image1, image2);
			logger.log(Level.INFO, "Progress: " + processed + "/" + images.size());
		}
	}


	protected void compressImage(String fileName, String fileName2) {
		Process proc = null;
		Process proc2 = null;

		try {
			long begin = System.currentTimeMillis();
			proc = startProcess(fileName);
			if (fileName2 != null) {
				proc2 = startProcess(fileName2);
			}

			endProcess(proc, fileName, begin);
			if (fileName2 != null) {
				endProcess(proc2, fileName2, begin);
			}
		} catch (Exception e) {
			logger.warn("Could not execute pngout!", e);
		} finally {
			close(proc);
			close(proc2);
		}
	}


	private Process startProcess(String fileName) throws IOException {
		logger.log(Level.INFO, "Compressing \"" + fileName + "\"");
		return new ProcessBuilder(PNGOUT, fileName).start();
	}


	private void endProcess(Process proc, String fileName, long begin) throws InterruptedException {
		proc.waitFor();
		if (proc.exitValue() != 0) {
			logger.warn("Error executing PNGOUT on " + fileName);
		} else {
			long time = System.currentTimeMillis() - begin;
			BigDecimal timeInSeconds = new BigDecimal(time / 1000).setScale(1,
			    BigDecimal.ROUND_HALF_UP);
			logger.log(Level.INFO, "Finished Compressing \"" + fileName + "\" in " + timeInSeconds
			    + " [s]");
		}
	}


	private void close(Closeable c) {
		if (c != null) {
			try {
				c.close();
			} catch (IOException e) {
				// ignored
			}
		}
	}


	private void close(Process proc) {
		if (proc != null) {
			close(proc.getOutputStream());
			close(proc.getInputStream());
			close(proc.getErrorStream());
			proc.destroy();
		}
	}
}
